{ Основной Родительскй Класс формы заявления содержит элементы диалога
  которые определяют стркутуру заявления на регистрацию группы
  и общее поведение для дочерних форм. Для контроля версий стркутуры заявления
  существует свойство VersionData  и  VersionHistory

  Module is a part of project http://code.google.com/p/drivers-training-request
  Copyright (C) 2012 Leonid Vlasov leoniv@bk.ru

  This source is free software; you can redistribute it and/or modify it under
  the terms of the GNU General Public License as published by the Free
  Software Foundation; either version 2 of the License, or (at your option)
  any later version.

  This code is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
  details.

  A copy of the GNU General Public License is available on the World Wide Web
  at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
  to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  MA 02111-1307, USA.
}

unit genericrequestform;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  Grids, MaskEdit, EditBtn, ExtCtrls, ComCtrls, ActnList, Menus, fgl,
  commonetypes, db,LazUTF8,LCLType, Buttons,CladrFrm,ConsoleFrm, types,strutils;

type
  { TWidgetedLable }
  TWindgetMap = specialize TFPGMap<string,TComponent>;
  TWidgetArray = Array of TWinControl;
  { TGReqForm }

  TGReqForm = class(TForm)
    BossName: TEdit;
    btSchoolClassAddress: TButton;
    btSchoolClassAddress1: TButton;
    btTichersAdd: TButton;
    btTichersDel: TButton;
    btMastersAdd: TButton;
    btMastersDel: TButton;
    btVihiclesAdd: TButton;
    btVihiclesDel: TButton;
    btStudentsAdd: TButton;
    btStudentsDel: TButton;
    ButtPanel: TPanel;
    BossRole: TEdit;
    GroupCshedule: TEdit;
    GroupCatVihicle: TEdit;
    lbTichers: TLabel;
    lbMasters: TLabel;
    lbVihicles: TLabel;
    lbStudents: TLabel;
    lbGroupCshedule: TLabel;
    lbGroupCatVihicle: TLabel;
    lbGroupDateStart: TLabel;
    lbGroupDateEnd: TLabel;
    lbGroupExamDate: TLabel;
    GroupDateStart: TDateEdit;
    GroupDateEnd: TDateEdit;
    GroupExamDate: TDateEdit;
    OwnType: TComboBox;
    lbOwnType: TLabel;
    lbLicCatVihicle: TLabel;
    lbSchoolBossName: TLabel;
    lbGroupBossName: TLabel;
    lbAutodromAddress: TLabel;
    lbSchoolPhone: TLabel;
    lbSchoolBossRole: TLabel;
    lbSchoolClassAddress: TLabel;
    LicCatVihicle: TEdit;
    gbGroup: TGroupBox;
    lbBossRole: TLabel;
    lbBossName: TLabel;
    lbLicPeriod: TLabel;
    lbLicDateStart: TLabel;
    LicDateStart: TDateEdit;
    lbLicSer: TLabel;
    lbLicNumber: TLabel;
    LicSer: TMaskEdit;
    LicNumber: TMaskEdit;
    LicPeriod: TMaskEdit;
    bpTichers: TPanel;
    bpMasters: TPanel;
    bpVihicles: TPanel;
    bpStudents: TPanel;
    Tichers: TStringGrid;
    tshMasters: TTabSheet;
    tshVihicles: TTabSheet;
    Masters: TStringGrid;
    Vihicles: TStringGrid;
    Students: TStringGrid;
    Lists: TPageControl;
    SchoolBossName: TEdit;
    GroupBossName: TEdit;
    AutodromAddress: TEdit;
    SchoolPhone: TEdit;
    SchoolBossRole: TEdit;
    SchoolClassAddress: TEdit;
    SchoolName: TEdit;
    ShoolName: TEdit;
    gbSchool: TGroupBox;
    groupNumber: TMaskEdit;
    lbSchoolName: TLabel;
    lbgroupNumber: TLabel;
    tshTichers: TTabSheet;
    tshStudents: TTabSheet;
    procedure btSchoolClassAddressClick(Sender: TObject);
    procedure btGridAddClick(Sender: TObject);
    procedure btGridDelClick(Sender: TObject);
    procedure btVihiclesAddClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); Virtual;
    procedure FormCreate(Sender: TObject);
    Procedure GreedsOnExit(Sender:TObject);
    Procedure EditsOnChange(Sender:TObject);
    procedure GroupBossNameChange(Sender: TObject);
    procedure LicCatVihicleKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure LicCatVihicleKeyPress(Sender: TObject; var Key: char);
    procedure LicCatVihicleKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure LicCatVihicleUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char
      );
    procedure bpTichersClick(Sender: TObject);
    procedure SchoolNameChange(Sender: TObject);
    procedure StudentsEnter(Sender: TObject);
    procedure StudentsExit(Sender: TObject);
    procedure TichersValidateEntry(sender: TObject; aCol, aRow: Integer;
      const OldValue: string; var NewValue: String);
    procedure tshMastersContextPopup(Sender: TObject; MousePos: TPoint;
      var Handled: Boolean);
    procedure ListsChange(Sender: TObject);
    procedure TichersKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
      );
    procedure tshVihiclesContextPopup(Sender: TObject; MousePos: TPoint;
      var Handled: Boolean);
  private
    function GetVersion: String;
    function GetVersionHistory: String;
  protected
    { private declarations }
    { members }
    FDataSet: TDataSet;
    FFileName: String;
    FModifaed: Boolean;
    FUUID: String;
    WidgetMap: TWindgetMap;//список всех виджетов формы
    procedure SetDataSet(AValue: TDataSet);
    procedure SetFaileName(AValue: String);
    procedure SetModifaed(AValue: Boolean);
    { metods }
    procedure ShowErrorReport(Report: TMessagesArray; Summary: String); //summary сообщение результата выводимое в Futter консоли
    procedure FillWidgetMap();//Заполняет список всех виджетов формы
    function GetEmptyWidgetDelete():TWinControl;//Возвращает первый незаполненный виджет
    function EmptyWidgetMessage(w:TWinControl):String;//Формирует сообщение о незаполненном поле ввода. Каждому полю ввода соответствует метка TLable имя метки  = 'lb'+ИмяПоляВвода
    function CheckObjectFullData(var Report:TMessagesArray):Boolean;//Проверяет полноту данных объекта
    function CheckObjectCorrectData(var Report:TMessagesArray):Boolean;//Проверяет корректность данных объекта
    function GetEmptyWidgets():TWidgetArray;//Возвращает массив незаполненных виджетов
    function SaveToXML(Report:TMessagesArray;Const AFileName:String):Boolean;//Записывает заявление в файл
    procedure SetCaption(const C:TCaption);
    Procedure AddButton(B:TButton;ACaption:String;AOnClick:TNotifyEvent;AHint:String='';AAlign:TAlign=alRight);
    //function ValToString(S:String;AType:TBaseType):String;Overload; //Возвращает значение поля ввода приведённое к нормализованной строке
    //function ValToString(S:String;AType:TBaseType):String;Overload; //Возвращает значение поля ввода приведённое к нормализованной строке
    //function GridToString(w:TCustomGrid;Row,Cel:Integer;AType:TBaseType):String;//Возвращает значение ячейки Grid приведённое к нормализованной строке
  public
    property FileName:String read FFileName write SetFaileName;
    property Modifaed:Boolean read FModifaed write SetModifaed;
    Property VersionData:String read GetVersion;//Версия структуры данных заявления
    Property VersionDHistory:String read GetVersionHistory;//История изменения в версиях структуры данных заявления
    function ReadFromXML(var Report:TMessagesArray; Const AFileName:String):Boolean;//Заполняет форму заявления из файла
  end;
implementation

{ TWidgetedLable }


{$R *.lfm}

{ TGReqForm }

procedure TGReqForm.FormCreate(Sender: TObject);
var
y,d,m:Word;
Ot:TOwnType;
begin
  WidgetMap:=TWindgetMap.Create;
  FillWidgetMap();
  DecodeDate(now,y,m,d);
  groupNumber.Text:='/'+IntToStr(y);
  for Ot in TOwnType do
  begin
    OwnType.Items.Add(OwnerTypeEnum[Ot]);
    OwnType.ItemIndex:=-1;
  end;
end;

procedure TGReqForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin

end;

procedure TGReqForm.btSchoolClassAddressClick(Sender: TObject);
var
   CladrForm:TCladrForm;
begin
   CladrForm:=TCladrForm.Create(Self);
   CladrForm.ShowModal;
   CladrForm.Free;
end;

procedure TGReqForm.btGridAddClick(Sender: TObject);
var
   Grid:TComponent;
   Key:Word;
begin
   Key:=VK_INSERT;
   Grid:=WidgetMap.KeyData[TrimRightSet(TrimLeftSet(TComponent(Sender).Name,['b','t']),['A','d'])];
   if TStringGrid(Grid).OnKeyDown = Nil then exit;
   TStringGrid(Grid).OnKeyDown(Grid,Key,[ssCtrl]);
end;

procedure TGReqForm.btGridDelClick(Sender: TObject);
var
   Grid:TComponent;
   Key:Word;
begin
  Key:=VK_DELETE;
   Grid:=WidgetMap.KeyData[TrimRightSet(TrimLeftSet(TComponent(Sender).Name,['b','t']),['D','e','l'])];
   if TStringGrid(Grid).OnKeyDown = Nil then exit;
   TStringGrid(Grid).OnKeyDown(Grid,Key,[ssCtrl]);
end;

procedure TGReqForm.btVihiclesAddClick(Sender: TObject);
begin

end;

procedure TGReqForm.FormCloseQuery(Sender: TObject; var CanClose: boolean);
begin
  if Modifaed then
     CanClose:=(QuestOnQuitWitoutSaving() = mrYes)
  else
      CanClose:=(QuestOnQuit() = mrYes);
end;

function TGReqForm.GetVersion: String;
begin
 Result:='1.0';
end;

function TGReqForm.GetVersionHistory: String;
begin
 Result:='';
end;


procedure TGReqForm.SetFaileName(AValue: String);
begin
  if FFileName=AValue then Exit;
  FFileName:=AValue;
end;

procedure TGReqForm.SetModifaed(AValue: Boolean);
var
  S:String;
begin
  if FModifaed = AValue then exit;
  FModifaed:=AValue;
  if FModifaed then
     Begin
          Caption:=Caption+'*'
     end
  else
     begin
       S:=Caption;
       Delete(S,Length(S)-1,1);
       Caption:=S;
     end;
end;

procedure TGReqForm.SetDataSet(AValue: TDataSet);
begin
  if FDataSet=AValue then Exit;
  FDataSet:=AValue;
end;

procedure TGReqForm.FillWidgetMap;
var
  i: Integer;
begin
  for i := 0 to ComponentCount-1 do
  begin
  if Not IsWidgetClass(Components[i]) Then
     begin
       {$IFDEF DEBUG}
         ToConsole('Skip widget: '+Components[i].Name+' Type '+Components[i].ToString,MessageTypeEnum.Debug);
       {$ENDIF}
       Continue;
     end;
  if Components[i] is TCustomEdit then
   begin
     TCustomEdit(Components[i]).OnChange:=@EditsOnChange;
   end;
  if Components[i] is TCustomGrid then
   begin
     TCustomGrid(Components[i]).OnExit:=@GreedsOnExit;
   end;
  WidgetMap.KeyData[Components[i].Name]:=Components[i];
  {$IFDEF DEBUG}
    ToConsole('Add widget: '+Components[i].Name+' Type '+Components[i].ToString,MessageTypeEnum.Debug);
  {$ENDIF}
  end;
end;

function TGReqForm.GetEmptyWidgetDelete: TWinControl;
var
  i: Integer;
  Widget:TWinControl;
begin
  for i := 0 to WidgetMap.Count-1 do
  begin
  if Not (WidgetMap.Data[i] is TWinControl) then Continue;
    Widget:= TWinControl(WidgetMap.Data[i]);
    case TComponent(Widget).ClassName of
      wtComboBox:if commonetypes.IsEmtyStr(TComboBox(Widget).Text) then begin Result:=Widget; Exit; end;
      wtEdit: if commonetypes.IsEmtyStr(TEdit(Widget).Text) then begin Result:=Widget; Exit; end;
      wtMaskEdit:  if commonetypes.isEmtyStr(TMaskEdit(Widget).Text) then begin Result:=Widget; Exit; end;
      wtGreed: if commonetypes.IsEmtyGreed(TCustomStringGrid(Widget)) then begin Result:=Widget; Exit; end;
      wtMemo:;
      wtDateEdit: if commonetypes.IsEmptyDate(TDateEdit(Widget).Date) then begin Result:=Widget; Exit; end;
    else
      Result:=nil;
    end;
  end;
end;

function TGReqForm.EmptyWidgetMessage(w: TWinControl): String;
var
  i: Integer;
begin
  try
   If (w is TCustomEdit) or (w is TCustomComboBox) then
      Result:=dlgoFillEmpty+TLabel(WidgetMap.KeyData['lb'+w.Name]).Caption
   else if w is TCustomGrid then
      Result:=dlgoGreedEmpty+TLabel(WidgetMap.KeyData['lb'+w.Name]).Caption;
  Except
    {$IFDEF DEBUG}
        ShowMessage(dlgoLableNotFound+w.Name);
    {$ELSE}
       Result:=dlgoUnknownEmpty;
    {$ENDIF}
  end;
end;

function TGReqForm.CheckObjectCorrectData(var Report: TMessagesArray): Boolean;
var
  Mess:TConsoleMsg;
begin
  {TODO -o:a -c:c : Rеализовать проверку дата начала < даты окончания }
  {TODO 0 -o: -c: : Реализовать проверку фдресов на соответстви кладр }
  {TODO  -o: -c: : Проверка формата даты рождения курсантов }
  {TODO  -o: -c: : Проверка формата даты рождения инструкторов }
  {TODO  -o: -c: : ФорматНомераГруппы}
  Result:=true;
end;

function TGReqForm.CheckObjectFullData(var Report:TMessagesArray): Boolean;
var
  EmptyWidgets:TWidgetArray;
  i: Integer;
  Mess:TConsoleMsg;
  Row,Col: Integer;
begin
  EmptyWidgets:=GetEmptyWidgets();
  Result:= (Length(EmptyWidgets)=0);
  for i := 0 to Length(EmptyWidgets)-1 do
  begin
  {TODO  -o: -c: : Реализовать сообщение об ошибке в зависимости от типа виджета  Для greed надо находить ячейку. }
      SetLength(Report,Length(Report)+1);
      Mess:=TEditConsoleMsg.Create(EmptyWidgetMessage(EmptyWidgets[i]),msgError,EmptyWidgets[i]);
      Report[Length(Report)-1]:=Mess;
       If EmptyWidgets[i] is TCustomGrid then
       begin
         {TODO  -o: -c: : Разбор грида по незаполненным ячейкам }
         for Row:= TStringGrid(EmptyWidgets[i]).FixedRows to TStringGrid(EmptyWidgets[i]).RowCount-1 do
             for Col:= TStringGrid(EmptyWidgets[i]).FixedCols to TStringGrid(EmptyWidgets[i]).ColCount-1 do
             begin
               if IsEmtyStr(TStringGrid(EmptyWidgets[i]).Cells[Col,Row]) then
                  begin
                    SetLength(Report,Length(Report)+1);
                    { TODO -oAuthor : Переделать проверку полей через объект класса TFieldDesc }
                    Mess:=TGreedConsoleMsg.Create(FOrmat(dlgoCellEmptyFormat,[Row,TStringGrid(EmptyWidgets[i]).Columns[Col].Title.Caption]),msgError,EmptyWidgets[i],Col,Row);
                    Report[Length(Report)-1]:=Mess;
                  end;
             end;
       end;
  end;
 end;

function TGReqForm.GetEmptyWidgets: TWidgetArray;
  procedure AddWidget(var a:TWidgetArray;w:TWinControl);
  begin
     SetLength(a,Length(a)+1);
     a[Length(a)-1]:=w;
  end;
var
  i: Integer;
  Widget:TWinControl;
  Str:String;
begin
  for i := 0 to WidgetMap.Count-1 do
  begin
  {$IFDEF DEBUG}
    ToConsole('widget: '+WidgetMap.Data[i].Name,MessageTypeEnum.Debug);
  {$ENDIF}
   if Not (WidgetMap.Data[i] is TWinControl) then Continue;
  Widget:= TWinControl(WidgetMap.Data[i]);
    case TComponent(Widget).ClassName of
      wtComboBox: if commonetypes.IsEmtyStr(TComboBox(Widget).Text) then begin AddWidget(Result,Widget); end;
      wtEdit: if commonetypes.IsEmtyStr(TEdit(Widget).Text) then begin AddWidget(Result,Widget); end;
      wtMaskEdit: if commonetypes.IsEmtyMaskEdit(TMaskEdit(Widget)) then begin AddWidget(Result,Widget); end;
      wtGreed: if commonetypes.IsEmtyGreed(TCustomStringGrid(Widget)) then begin AddWidget(Result,Widget); end;
      wtMemo:;
      wtDateEdit: if commonetypes.IsEmptyDate(TDateEdit(Widget).Date) then begin AddWidget(Result,Widget); end;
    end;
  end;
end;

function TGReqForm.SaveToXML(Report:TMessagesArray; const AFileName: String): Boolean;
var
  XMLStream:String;
begin
  {TODO  -o: -c: : Запись в файл }

  Result:=True;
  FFileName:=AFileName;
  SetCaption(FFileName);
  Modifaed:=False;
end;

procedure TGReqForm.SetCaption(const C: TCaption);
begin
    Caption:=dlgoDefFormCaptionPrefix+C;
end;

function TGReqForm.ReadFromXML(var Report: TMessagesArray;
  const AFileName: String): Boolean;
begin
  {TODO  -o: -c: : Чтение из файла }

  Result:=True;
  FFileName:=AFileName;
  SetCaption(FFileName);
  Modifaed:=False;
end;

procedure TGReqForm.AddButton(B:TButton;ACaption:String;AOnClick:TNotifyEvent;AHint:String='';AAlign:TAlign=alRight);
begin
     B:=TButton.Create(ButtPanel);
     B.Caption:=ACaption;
     B.OnClick:=AOnClick;
     B.Align:=AAlign;
     B.AutoSize:=True;
     B.Hint:=AHint;
     B.Parent:=ButtPanel;
end;

procedure TGReqForm.EditsOnChange(Sender: TObject);
begin
   Modifaed:=True;
end;

procedure TGReqForm.GroupBossNameChange(Sender: TObject);
begin

end;

procedure TGReqForm.LicCatVihicleKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if NOT (Key in [VK_A..VK_E,VK_DELETE,VK_BACK,VK_LEFT,VK_RIGHT,VK_TAB]) then
     Key:=0;
end;

procedure TGReqForm.LicCatVihicleKeyPress(Sender: TObject; var Key: char);
begin
end;

procedure TGReqForm.LicCatVihicleKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
end;

procedure TGReqForm.LicCatVihicleUTF8KeyPress(Sender: TObject;
  var UTF8Key: TUTF8Char);
begin
  case UTF8Key of
    #209#132: UTF8Key:= 'A';
    'И': UTF8Key:= 'B';
    'С': UTF8Key:= 'C';
    'В': UTF8Key:= 'D';
    'У': UTF8Key:= 'E';
    'Ф': UTF8Key:= 'A';
    'и': UTF8Key:= 'B';
    'с': UTF8Key:= 'C';
    'в': UTF8Key:= 'D';
    'у': UTF8Key:= 'E';
  end;
  if Pos(UpperCase(UTF8Key),TEdit(Sender).Text)>0 then
    UTF8Key:=#0;
end;

procedure TGReqForm.bpTichersClick(Sender: TObject);
begin

end;

procedure TGReqForm.SchoolNameChange(Sender: TObject);
begin

end;

procedure TGReqForm.StudentsEnter(Sender: TObject);
begin

end;

procedure TGReqForm.StudentsExit(Sender: TObject);
begin
end;

procedure TGReqForm.TichersValidateEntry(sender: TObject; aCol, aRow: Integer;
  const OldValue: string; var NewValue: String);
begin
end;

procedure TGReqForm.tshMastersContextPopup(Sender: TObject; MousePos: TPoint;
  var Handled: Boolean);
begin

end;

procedure TGReqForm.ListsChange(Sender: TObject);
begin

end;

procedure TGReqForm.TichersKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin

end;

procedure TGReqForm.tshVihiclesContextPopup(Sender: TObject; MousePos: TPoint;
  var Handled: Boolean);
begin

end;

procedure TGReqForm.GreedsOnExit(Sender: TObject);
begin
  if Not Modifaed = true then
   Modifaed:=True;
end;

procedure TGReqForm.ShowErrorReport(Report:TMessagesArray;Summary:String);
var
  i: Integer;
begin
  if  Length(Report) = 0 Then exit;
  {$IFNDEF DEBUG}
   ConsoleForm.Clear;
  {$ENDIF}
  for i:= 0 to Length(Report) - 1 do
  begin
    ConsoleForm.AddMessage(Report[i]);
  end;
  ConsoleForm.AddFutter(Summary);
  SetLength(Report,0);
  if NOT ConsoleForm.Visible then
     ConsoleForm.Show;
   ConsoleForm.SetFocus;
end;


end.

